package com.lei.mydb.backend.tm;


import com.lei.mydb.backend.utils.Panic;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.lei.mydb.common.Error;
import com.lei.mydb.backend.utils.Parser;

/**
 * @author lbwxxc
 * @date 2025/4/26 12:30
 * @description: 事务管理实现类
 */
public class TransactionManagerImpl implements TransactionManager {

    // XID文件头长度
    static final int LEN_XID_HEADER_LENGTH = 8;
    // 每个事务的占用长度
    private static final int XID_FIELD_SIZE = 1;

    // 事务的三种状态
    // 活跃
    private static final byte FIELD_TRAN_ACTIVE   = 0;
    // 提交
    private static final byte FIELD_TRAN_COMMITTED = 1;
    // 撤销
    private static final byte FIELD_TRAN_ABORTED  = 2;

    // 超级事务，永远为commited状态
    public static final long SUPER_XID = 0;

    // xid 事务文件后缀
    static final String XID_SUFFIX = ".xid";

    // xid 事务文件
    private RandomAccessFile file;
    // xid 文件读取
    private FileChannel fc;
    // 当前事务
    private long xidCounter;
    // 锁防止并发问题
    private Lock counterLock;

    public TransactionManagerImpl(RandomAccessFile file, FileChannel fc) {
        this.fc = fc;
        this.file = file;
        counterLock = new ReentrantLock();
        this.checkXIDCounter();
    }

    // 保证每次只有一个线程开启事务
    @Override
    public long begin() {
        counterLock.lock();
        try {
            long xid = this.xidCounter + 1;
            this.updateXID(xid, FIELD_TRAN_ACTIVE);
            this.incrXIDCounter();
            return xid;
        }  finally {
            counterLock.unlock();
        }
    }

    @Override
    public void commit(long xid) {
        updateXID(xid, FIELD_TRAN_COMMITTED);
    }

    @Override
    public void abort(long xid) {
        updateXID(xid, FIELD_TRAN_ABORTED);
    }

    @Override
    public boolean isActive(long xid) {
        return checkXID(xid, FIELD_TRAN_ACTIVE);
    }

    @Override
    public boolean isCommitted(long xid) {
        return checkXID(xid, FIELD_TRAN_COMMITTED);
    }

    @Override
    public boolean isAborted(long xid) {
        return checkXID(xid, FIELD_TRAN_ABORTED);
    }

    @Override
    public void close() {
        try {
            fc.close();
            file.close();
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /**
     * @param:
     * @return: void
     * @description: 先通过 fc 获取 xid 文件的实际大小为 fileLen，然后通过头字节获得当前的事务 id，然后算出这个 id 在 xid 文件的位置为 end，
     * 如果 fileLen != end，说明 xid 文件异常，直接退出系统
     */
    private void checkXIDCounter() {
        // 实际文件长度
        long fileLen = 0;
        try {
            fileLen = file.length();
        } catch (IOException e) {
            Panic.panic(Error.BadXIDFileException);
        }
        if (fileLen < LEN_XID_HEADER_LENGTH) {
            Panic.panic(Error.BadXIDFileException);
        }

        // 理论长度
        ByteBuffer byteBuffer = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
        try {
            fc.position(0);
            fc.read(byteBuffer);
        } catch (IOException e) {
            Panic.panic(e);
        }
        // 读取头字节的xidCounter，存入到内存
        this.xidCounter = Parser.parseLong(byteBuffer.array());
        long end = this.getXidPosition(xidCounter + 1);

        // 如果不相等说明 xid 文件出现错误，直接退出系统
        if (end != fileLen) {
            Panic.panic(Error.BadXIDFileException);
        }
    }

    /**
     * @param: xid
     * @param: status
     * @return: void
     * @description: 更新事务的状态
     */
    private void updateXID(long xid, byte status) {
        long offset = getXidPosition(xid);
        byte[] tmp = new byte[XID_FIELD_SIZE];
        tmp[0] = status;

        try {
            ByteBuffer byteBuffer = ByteBuffer.wrap(tmp);
            fc.position(offset);
            fc.write(byteBuffer);
        } catch (IOException e) {
            Panic.panic(e);
        }

        try {
            //强制同步缓存内容到文件中，类似于 BIO 中的 flush() 方法。force 方法的参数是一个布尔，表示是否同步文件的元数据（例如最后修改时间等）
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /**
     * @param:
     * @return: void
     * @description: 将XID加一，并更新XID Header
     */
    private void incrXIDCounter() {
        xidCounter++;
        ByteBuffer byteBuffer = ByteBuffer.wrap(Parser.long2Byte(xidCounter));

        try {
            fc.position(0);
            fc.write(byteBuffer);
        } catch (IOException e) {
            Panic.panic(e);
        }

        try {
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    /**
     * @param: xid
     * @param: status
     * @return: boolean
     * @description: 检测 XID 事务是否处于 status 状态
     */
    private boolean checkXID(long xid, byte status) {
        long offset = getXidPosition(xid);
        byte[] tmp = new byte[XID_FIELD_SIZE];
        ByteBuffer byteBuffer = ByteBuffer.wrap(tmp);
        try {
            fc.position(offset);
            fc.read(byteBuffer);
        } catch (IOException e) {
            Panic.panic(e);
        }
        return byteBuffer.array()[0] == status;
    }

    // 根据事务xid取得其在xid文件中对应的位置
    private long getXidPosition(long xid) {
        return LEN_XID_HEADER_LENGTH + (xid - 1) * XID_FIELD_SIZE;
    }

}
